/******************************************************************************
 *
 * @copyright:          Copyright 2011 UniSky Ltd.
 * @file:
 * @author:              kenping.liu
 * @creation:
 * @description:
 *
 * @modifications
 * date         user                description
 *
 *
 * @more-details
 *
 *****************************************************************************/

#include "u/u_sys.h"
#include "u/u_logger.h"

U_NAMESPACE_BEGIN(u)
U_NAMESPACE_BEGIN(sys)


///////////////////////////////////////////////////////////////////////////////
int OS::set_coredump_path(const char *path)
{
#if defined(WINDOWS)
#else
    struct rlimit rlim;
	char cmd[MAX_PATH];

	memset(&rlim, 0, sizeof(struct rlimit));
	rlim.rlim_cur = RLIM_INFINITY;
	rlim.rlim_max = RLIM_INFINITY;
	if (setrlimit(RLIMIT_CORE, &rlim) < 0)
    {
		u::Log::error("setrlimit() failed: %s\n", strerror(errno));
		return -1;
	}
	sprintf(cmd, "echo \"%s/core.%%e.%%s.%%p.%%t\" > /proc/sys/kernel/core_pattern", path);
	int ret = system(cmd);
	if (-1 == ret)
	{
		perror("system");
	}

#endif
	return NO_ERROR;
}

///////////////////////////////////////////////////////////////////////////////
#if defined(WINDOWS)
Lock::Lock(const char* name)
{
    _mtx = ::CreateMutex(NULL, FALSE, name);
}

Lock::~Lock()
{
    if (_mtx != NULL)
    {
        ::CloseHandle(_mtx);
        _mtx = NULL;
    }
}

bool Lock::lock()
{
    DWORD dwRet = ::WaitForSingleObject(_mtx, INFINITE);
    return (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED);
}

bool Lock::try_lock()
{
    return false;
}

bool Lock::unlock()
{
    return TRUE==::ReleaseMutex(_mtx);
}
#else
Lock::Lock(const char* name)
{
    pthread_mutex_init(&_mtx, NULL);
}

Lock::~Lock()
{

    pthread_mutex_destroy(&_mtx);
}

bool Lock::lock()
{
    return pthread_mutex_lock(&_mtx);
}

bool Lock::try_lock()
{
    return pthread_mutex_trylock(&_mtx);
}

bool Lock::unlock()
{
    return pthread_mutex_unlock(&_mtx);
}
#endif
///////////////////////////////////////////////////////////////////////////////
AutoLock::AutoLock(Lock* lck)
{
    _lck = lck;
    if ( NULL!=_lck )
    {
        _lck->lock();
    }
};

AutoLock::~AutoLock()
{
    if ( NULL!=_lck )
    {
        _lck->unlock();
    }
    _lck = NULL;
};

///////////////////////////////////////////////////////////////////////////////
#if defined(WINDOWS)
Event::Event()
{
    _evt = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}

Event::~Event()
{
    if ( _evt )
    {
        ::CloseHandle(_evt);
    }
    _evt = NULL;
}

bool Event::set()
{
    return _evt? (TRUE==::SetEvent(_evt)) : false;
}

bool Event::reset()
{
    return _evt? (TRUE==::ResetEvent(_evt)) : false;
}

DWORD Event::wait(DWORD mills)
{
    return _evt? ::WaitForSingleObject(_evt, mills) : 0;
}

///////////////////////////////////////////////////////////////////////////////
Semaphore::Semaphore(const char* name)
{
    _semaphore = NULL;
    ZeroMemory(_name, 128);
    u_sprintf(_name, "%s", name);
}

Semaphore::~Semaphore()
{
    destroy();
}

bool Semaphore::create()
{
    if ( NULL==(_semaphore=::OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, _name)) )
    {
        _semaphore = ::CreateSemaphore(NULL, 1, 1, _name);
        if ( ERROR_ALREADY_EXISTS==::GetLastError() )
        {
            destroy();
        }
    }
    return NULL!=_semaphore;
}

void Semaphore::destroy()
{
    if ( _semaphore )
    {
        ::CloseHandle(_semaphore);
        _semaphore = NULL;
    }
}

void Semaphore::wait()
{
    if ( _semaphore )
    {
        ::WaitForSingleObject(_semaphore, 0);
    }
}

#else
Event::Event(){}
Event::~Event(){}
bool Event::set(){return false;}
bool Event::reset(){return false;}
DWORD Event::wait(DWORD mills) {return 0;}

Semaphore::Semaphore(const char* name){}
Semaphore::~Semaphore() { destroy(); }
bool Semaphore::create() { return true; }
void Semaphore::destroy(){}
void Semaphore::wait(){}
#endif

///////////////////////////////////////////////////////////////////////////////

#if defined(WINDOWS)
Thread::Thread(const char* name, bool auto_del) : _tid(0), _thread(NULL), _running(false), _auto_del(auto_del)
#else
Thread::Thread(const char* name, bool auto_del) : _tid(0), _running(false), _auto_del(auto_del)
#endif
{
    u_sprintf(_name, "%s", name);
    _thread = 0;
    pthread_mutex_init(&m_mutex, NULL);
    pthread_cond_init(&m_cond, NULL);
};

#if defined(WINDOWS)
Thread::~Thread()
#else
Thread::~Thread()
#endif
{
    _running = false;
    terminate();
#if defined(WINDOWS)
    _thread = NULL;
#else
#endif
    _tid = 0;
    pthread_mutex_destroy(&m_mutex);
    pthread_cond_destroy(&m_cond);
};

void Thread::Delay(int mseconds)
{
	struct timeval tp;
	gettimeofday(&tp, NULL);

	struct timespec timeout;
	timeout.tv_sec = tp.tv_sec;
	timeout.tv_nsec = tp.tv_usec * 1000L;

	timeout.tv_nsec += (int64_t)mseconds * 1000000L;

	timeout.tv_sec += timeout.tv_nsec / 1000000000L;
	timeout.tv_nsec = timeout.tv_nsec % 1000000000L;

	pthread_mutex_lock(&m_mutex);
	pthread_cond_timedwait( &m_cond, &m_mutex, &timeout );
	pthread_mutex_unlock(&m_mutex);
}

void Thread::WakeUp()
{
	pthread_mutex_lock(&m_mutex);
	pthread_cond_signal(&m_cond);
	pthread_mutex_unlock(&m_mutex);
}

#if defined(WINDOWS)
DWORD WINAPI Thread::thread_proc(LPVOID param)
#else
void* Thread::thread_proc(void* param)
#endif
{
    if ( NULL!=param )
    {
        Thread* thd = (Thread*)param;
        thd->_running = true;
        thd->resume();

        bool need_delete = thd->_auto_del;

        char tmp[128]    = {0};
        u_sprintf(tmp, "uthread[%s: %lu(0x%lx)]", thd->_name, thd->_thread, thd->_thread);

        u_sprintf(tmp, "uthread[%s]", thd->_name);
//        u::Log::info("[comm] begin uthread: %s", tmp);

#if defined(WINDOWS)
        HANDLE handle = thd->_thread;
#endif

        thd->run();

#if defined(WINDOWS)
        CloseHandle(handle);
        thd->_thread = NULL;
        thd->_tid    = 0;
#endif

//        u::Log::info("[comm] exit uthread: %s", tmp);

        if ( need_delete && thd )
        {
            try
            {
                delete thd;
            }
            catch (...)
            {
            }
        }
    }
#if defined(WINDOWS)
    return 0;
#else
    return (void*)0;
#endif
}

void Thread::begin()
{
    if ( !_running )
    {
#if defined(WINDOWS)
        DWORD tid = 0;
        _thread = ::CreateThread(NULL,                   // default security attributes
            0,                      // use default stack size
            Thread::thread_proc,    // thread function
            this,                   // argument to thread function
            0,                      // use default creation flags
            &tid);                  // returns the thread identifier
        _tid = tid;
        if ( NULL==_thread )
        {
            u::Log::warn("[comm] create uthread[%s] failed: %s", _name, u::get_error_msg());
        }
#else
        pthread_create(&_thread, NULL, Thread::thread_proc, this);
        while (!_running)
        {
        	usleep(1000);
        }
#endif
    }
    else
    {
        resume();
    }
}

void Thread::tryterminate()
{
	_running = false;
}

void Thread::terminate()
{
    if ( _running )
    {
        _running = false;
        resume();
    }
    WakeUp();
#if defined(WINDOWS)
    if ( _thread )
    {
        ::WaitForSingleObject(_thread, 100);

        if ( _running )
        {
            _running = false;

            DWORD dwExitCode = 0;
            GetExitCodeThread(_thread, &dwExitCode);
            if ( dwExitCode==STILL_ACTIVE )
            {
                TerminateThread(_thread, DWORD(-1));

                CloseHandle(_thread);
                _tid = 0;
                if ( _auto_del )
                {
                    delete this;
                }
            }
        }
    }
#else
    if (_thread != 0)
    {
		pthread_join(_thread, NULL);
		_thread = 0;
    }
#endif
}

void Thread::suspend()
{
    _event.reset();
}

void Thread::resume()
{
    _event.set();
}

void Thread::wait(UINT mills)
{
    if ( 0>=mills )
    {
        _event.wait();
    }
    else
    {
        _event.wait(mills);
    }
}

U_NAMESPACE_END(sys)
U_NAMESPACE_END(u)
